定制模块
- 对于定制的 IDE 产品,基于 Theia 应该是最多的。这几年 OpenSumi 非常火,它比 Theia 的定制成本更加低,性能更加好。业界基于 VS Code 魔改最成功的产品严格意义上只有 Azure 的 DataStudio,Code Server 和 Gitpod 对于源码定制部分其实不多。但是我们却选择了 VS Code,一方面是我们最终克服了对于修改 VS Code 源码的恐惧,做到了很少有人做的事情。其次是我们认为我们还是走在对的路上的,我们认为在我们这种玩法下, MC/EMR/SQL 这一类的特定引擎语言 IDE 想要落地,其实成本也没有那么高了。
- 我们走通了一条路: 从开源中来,再回馈到开源社区。
难点
1. 深入定制的难点
1. 深入定制没有文档
深入 UI 定制
比如我们在最开始觉得直接使用原生的 DOM 开发 UI (在底座中)是一种极其低效的方式,所以做过两种尝试:
- 想通过在扩展中透传 React 组件的信息过来,然后在底座中进行渲染,但是这种方式在实现上有很多的问题,比如:扩展与底座的交互,可以通过 SDK 进行数据交换,但是也仅限于可以进行序列化和反序列化的对象、字符串。 我最开始天真的觉得,将 React 的虚拟 DOM 对象传入,在底座中引入 ReactDOM 直接就可以渲染。实际上,React 虚拟节点中携带类似
$$xxx的属性,经过 SDK 传参之后丢失,底座根本拿不到一个正常可渲染的节点。这样的想法,是出于对架构的不了解:- 就算在底座中填补上虚拟节点的
$$xxx的属性,还是无法保证在通信过程中是否有其他属性会丢失,导致渲染节点失败。 - 后来了解了 VS Code 与 Extension 之间的架构之后,了解到实际上扩展与底座之间是通过 RPC 通信,字符串可以正常传,对象被序列化之后传,而函数,也需要特殊处理,只有
$开头的函数,在主进程和扩展进程中才可以互相调用,普通函数会被过滤。
- 就算在底座中填补上虚拟节点的
- 我想到的第二种方式,是直接在 VS Code Web 中引入 React + 组件库,画出一个 UI 页面来(当然这种方式当初也只是为了 demo)
- 第二步中遇到了一个棘手的问题,直接渲染 DOM 节点的 UI 无法正常滚动。后面研究了编辑器、文件树、编辑器顶部 Tab(可以滑动的模块)
- 为什么无法正常滚动? VS Code 整个模块全部禁用掉了上下左右的默认的滚动事件,稍微旧一点的版本 WebView 的左右滚动事件没有禁用,因此在 Webview 上可以左右后退(是一个 bug)。取而代之的是,VS Code 自行在 DOM 实现的事件监听。
- 那么要实现一个滚动模块要怎么做?
- 可滚动的 DOM:决定可滚动区域的高度,以及滚动高度,需要根据这两者渲染右侧滚动条的高度。
- 可滚动的对象: 决定当前的列表滚动到哪里了。
除此之外,我们还定制了编辑器顶部工具栏,右侧面板的 Panel 层。全都是按照 VS Code 的规范来玩的,但这些都没有透出文档,只能通过阅读源码来实现。
遇到这样的问题,前期比较痛苦,我们会以传统前端的思维去猜测我们不了解的功能是如何实现的,而且对于 VS Code 模块划分、DI 注入的这种方式(其实不能很好的通过 VS Code 原生的跳转,因为大量的 interface 影响跳转)
2. 构建与传统前端的差异
通过 gulp 构建,底座不是通过 Webpack 构建的。
- 云构建卡顿,很慢。 一次构建需要 20 分钟(本地构建 8 - 10 分钟,其余都是安装依赖的事件)左右。
- 样式隔离: VS Code 与扩展的 Webview 样式是隔离的,但是与业务 (公共头)默认在一张 html 里,样式会互相影响。
- 国际化: 底座、内建扩展、普通扩展国际化包的方式差异很大,需要将业务的国际化状态与 VS Code 底座进行同步。
- 底座默认开发环境只能英文,中文需要在构建过程中将国际化包准备好,再最后一步构建时处理。
- 内建扩展的国际化需要 gallery 支持,国际化包将 bundle 和 package 包统一提供了,但是 package 数据需要请求后端接口,再 replace 掉才能生效。
- 普通扩展的 package 和 bundle 文件全部由业务扩展自己提供。
- 部署方式的问题。
- 部门部署前端方式的问题,不能满足 Web Worker 的同域保证
3. 定期合并开源版本代码
- 使用开源软件有一个问题是,保证安全一般都会锁版本,但是锁版本了,就无法享受到新版本的功能,并且也可能会存在致命缺陷,如果很久没有更新,合并的时候就会很痛苦;但是如果不锁版本(合并代码比较频繁),那需要非常完善的测试机制,保证合并之后的产品质量。并且,需要将合并的时机,与我们的产品的发布时机契合的比较好。目前合并的时机策略:
- 定期(2-3 个月)合并一次
- 或者是有重大的 bug 修复的时候合并一次。
- 每月 VS Code 的版本更新日志我们会看,并在周会上给团队的同学介绍,如果有比较吸引我们的功能,我们也会主动求合入。
- 合并过程比较头大。 很多模块被我们修改了之后,可能新版本已经删除了,或者是修改了,我们需要重新修改。
- code 平台。。。 站点都无法显示一次 diff。代码量很大(通常是一两个月更新一次),几千个文件,几万行代码改动很正常。CR 的网站打开都很卡顿。
- 更新底座代码时,源码架构变化,文件移动、重命名等,代码合并的困难。 需要人工 recheck, 重新 review 功能。
- 合并之后的工程质量保证
- 合并代码并没有办法很好的解决。 但是对于功能的保证,可以通过单元测试、集成测试来保证改动不影响功能。分析了 VS Code 现有的测试方式: 单元测试、集成测试、扩展测试、冒烟测试。 我们按照他们的规范,写我们业务自己的单元测试和集成测试代码。
- CI 不能直接跑,也需要人工在本地执行。集团内的基建问题,Aone 一直没有自动执行的测试脚本的功能,所以我们只能暂时手动执行。持续集成的问题,我们在着手解决。
4. 克制
经过接近 2 年时间的摸索,其实对于目前的我来说,基本上对于底座的任何改动我都有信心可以按照 VS Code 的玩法进行魔改。但是,这样的改动,对于其他人来说,是不是也能够很好的接受呢?这是一个问题。在迁移新版数据开发的过程中,我们遇到很多比较模凌两可的交互,深入底座来修改也可以,由扩展来改变交互也可以。
例如,我们最近遇到一个问题,提出 sql 包的概念,将代码和配置文件统一成一个软件包,类似 jar 包,这一层会作为逻辑层,与传统的文件树有一定的差异,功能少了很多。
结果
- 核心产品灰度内测中;某产品接入之后,提升 xxx
- 扩展工程化支持,支持 x 扩展的工程,内、外上线。
- copilot 研究,支持 lsp 编辑器落地